#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>   //杂项设备头文件
#include <linux/fs.h>           //文件操作集
#include <linux/of.h>           //设备树操作函数操作集
#include <linux/uaccess.h>       //copy_to_user函数的文件

#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
 
//oled寄存器地址
#define OLED_CMD    0x00 //OLED写命令
#define OLED_DATA   0x40 //OLED写数据

#define Max_Column  128 //限制oled最大值

//static struct device_node *oled_device_node;//获取设备树节点
static struct i2c_client *oled_client;      //用于获取从设备元器件的地址

static void oled_write_reg(u8 reg_addr, u8 data, u8 len);

//向寄存器写数据函数
static void oled_write_reg(u8 reg_addr, u8 data, u8 len)
{
    u8 buff[256];
    struct i2c_msg msgs[] = {
    [0] = {
        .addr = oled_client->addr,
        .flags = 0,
        .len = len + 1,
        .buf = buff,
        }
    };

    buff[0] = reg_addr;
    memcpy(&buff[1], &data, len);//拼接buff可以在函数开始前进行
    i2c_transfer(oled_client->adapter, msgs, 1);//能够对 I2C 设备寄存器进行读写操作参数1表述获取一次从地址数据对其寄存器进行1次的读写
}

/********************对oled初始化操作*******************/
//对oled进行初始化
void oled_init(void)
{
    u8 i;
    u8 data[] ={ 
            0xae,0X00,0X10,0x40,0X81,0XCF,0xff,0xa1,0xa4,
            0xA6,0xc8,0xa8,0x3F,0xd5,0x80,0xd3,0x00,0XDA,
            0X12,0x8d,0x14,0xdb,0x40,0X20,0X02,0xd9,0xf1,0xaf };

    for(i=0; i<sizeof(data); i++)
    {
       oled_write_reg(OLED_CMD, data[i], 1);
    }

}

//oled清屏函数
void oled_clear(void)
{  
    u8 i, n;
    for(i=0; i<8; i++) 
    {  
       oled_write_reg(OLED_CMD, 0xb0 + i, 1);      //设置页地址（0~7）
       oled_write_reg(OLED_CMD, 0x00,   1);        //设置显示位置—列低地址
       oled_write_reg(OLED_CMD, 0x10,   1);        //设置显示位置—列高地址

       for(n=0; n<128; n++)
       {
           oled_write_reg(OLED_DATA, 0x00, 1);
       }
    }
}

//定位oled起始位置描绘内容函数设置光标
void oled_set_pos(u8 x, u8 y)
{  
    oled_write_reg(OLED_CMD, 0xb0 + y, 1);
    oled_write_reg(OLED_CMD, ((x & 0xf0) >> 4) | 0x10, 1);
    oled_write_reg(OLED_CMD, x & 0x0f, 1);
}
static u8 OLED_GRAM[8][128]; //定义映射的地址大小

//封装画点函数
void OLED_DrawPoint(u8 x,u8 y,u8 c)
{
	u8 page=0;
	page=y/8;//y坐标对应在哪一页
	//y=12,y/8=1,y%8=12%8=1....4
	y=y%8;//对应页上的哪一行6%8=0---6
	if(c)OLED_GRAM[page][x]|=1<<y;//点亮对应的点阵
	else OLED_GRAM[page][x]&=~(1<<y);//关闭对应的点阵
}
 
 /**************更新数据到屏幕*****************/
void OLED_RefreshGram(void)
{
	u8 i,j;
	for(i=0;i<8;i++)
	{
       oled_write_reg(OLED_CMD, 0xb0 + i, 1);      //设置页地址（0~7）
       oled_write_reg(OLED_CMD, 0x00,   1);        //设置显示位置—列低地址
       oled_write_reg(OLED_CMD, 0x10,   1);        //设置显示位置—列高地址
		for(j=0;j<128;j++)
            oled_write_reg(OLED_DATA,OLED_GRAM[i][j],OLED_DAT);
	}	
}

/*******************清屏函数*****************/
void OLED_ClearGram(void)
{
	memset(OLED_GRAM,0x0,sizeof(OLED_GRAM));//清除缓冲区
    //OLED_RefreshGram();
}

//fd_ops结构体的数据定义
#define OLED_REFLASH 0X80
static int oled_ioctl(struct fb_info *info, unsigned int cmd,unsigned long arg)
{
	int i,j;
	int w,h;
	w=info->var.xres;//屏幕宽
	h=info->var.yres;
	char *p=info->screen_base;
	//printk("w=%d,h=%d\n",w,h);
	switch(cmd)
	{
		case OLED_REFLASH://更新数据到屏幕
		for(i=0;i<h;i++)
		{
			for(j=0;j<w;j++)
			{
				if(*p)//查看buff是否有数据
				{
					OLED_DrawPoint(j,i,1);
				}
				else
				{
					OLED_DrawPoint(j,i,0);
				}
				p++;
			}
		}
		OLED_RefreshGram();//更新显示
		break;	
	}
	
	return 0;
}

/*fb_ops结构体定义*/
static struct fb_ops oled_fbops=
{
	.fb_ioctl=oled_ioctl
};

static struct fb_info fb_info=
{
	.var=
	{
		.xres=128,
		.yres=64,
		.bits_per_pixel=8,//8位表示一个像素点
	},
	.fix=
	{
		.id="ssd1306",
		//.smem_start= //物理地址
		.smem_len=128*64,//屏幕缓冲区大小
		.line_length=128,//一行的字节数
		
	},
	.fbops=&oled_fbops,//文件操作集合
	//screen_base 虚拟地址
};


int oled_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    printk("i2c_probe!\n");
    printk("misc register is ok\n");
    oled_client = client; //申请i2c的

    /*dma申请物理空间*/
	fb_info.screen_base=dma_alloc_writecombine(NULL,fb_info.fix.smem_len,(dma_addr_t *)&fb_info.fix.smem_start,GFP_KERNEL);
    /*注册帧缓冲驱动*/
	register_framebuffer(&fb_info);
    
    printk("oled_i2c_ok\n");
    oled_init();
    return 0;
}

/* i2c 驱动的 remove 函数 */
int oled_remove(struct i2c_client *i2c_client)
{   
    printk("oled_remove 资源释放成功\n");
	/*注销帧缓冲设备*/
	unregister_framebuffer(&fb_info);
	/*释放空间*/
	dma_free_writecombine(NULL,fb_info.fix.smem_len,fb_info.screen_base,fb_info.fix.smem_start);

    return 0;
}

//与设备树的 compatible 匹配
static const struct of_device_id oled_id[] = {
{.compatible = "edt,edt-oled", 0},
{.compatible = "edt,edt-oled", 0},
{.compatible = "edt,edt-oled", 0},
{}};

// 无设备树的时候匹配 ID 表
static const struct i2c_device_id oled_id_ts[] = {
{"edt,edt-oled", 0},
{}};

//定义一个 i2c_driver 的结构体
static struct i2c_driver oled_driver = {
    .driver = {
        .owner = THIS_MODULE,
        .name = "oled_test",
        .of_match_table = oled_id,// 采用设备树的时候驱动使用的匹配表
    },
    .probe = oled_probe,
    .remove = oled_remove,
    .id_table = oled_id_ts
};

/* 驱动入口函数 */
static int oled_driver_init(void)
{
    int ret=0;
    //注册 i2c_driver
    ret = i2c_add_driver(&oled_driver);//
    if (ret < 0)
    {
        printk(" i2c_add_driver is error \n");
        return ret;
    }
    printk("This is oled_driver_init\n");
    return ret;
}

/* 驱动出口函数 */
static void oled_driver_exit(void)
{
    printk("This is oled_driver_exit\n");
    i2c_del_driver(&oled_driver);
}

module_init(oled_driver_init);
module_exit(oled_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("CLING");